package com.djicorp.rm.web.api.aspect;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.djicorp.rm.web.api.common.SpringContextHolder;
import com.djicorp.rm.web.api.entity.DataAuditLog;
import com.djicorp.rm.web.api.mapper.DataAuditLogMapper;
import com.google.gson.Gson;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Slf4j
@Aspect
@Component
public class DataAuditAspect {

    @Resource
    private DataAuditLogMapper dataAuditLogMapper;
    @Resource
    private Gson gson;

    // 定义Mapper层切入点
    @Pointcut("execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.insert*(..)) || " +
            "execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.update*(..)) || " +
            "execution(* com.djicorp.rm.web.api.mapper.*Mapper.delete*(..))")
//            "execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.delete*(..))")
    public void dataAuditPointcut() {}

    // 环绕通知处理审计逻辑
    @Around("dataAuditPointcut()")
    public Object aroundDataAuditAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();

        // 获取操作类型
        String operationType = resolveOperationType(method);

        // 获取实体对象（假设第一个参数为实体）
        Object entity = args.length > 0 ? args[0] : null;
        if (entity == null) return joinPoint.proceed();

        // 获取表元数据
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
        if (tableInfo == null) return joinPoint.proceed();

        // 如果是审计表自身的增删改则直接返回，否则进入死循环
        if(tableInfo.getEntityType().equals(DataAuditLog.class)){
            return joinPoint.proceed();
        }

        // 获取变更前数据
        Object oldData = getOriginalData(operationType, tableInfo, entity);

        // 执行原操作
        Object result = joinPoint.proceed();

        // 异步保存日志
        saveAuditLogAsync(tableInfo, entity, oldData, operationType);

        return result;
    }

    // 解析操作类型
    private String resolveOperationType(Method method) {
        if (method.isAnnotationPresent(Insert.class) || method.getName().startsWith("insert")) return "INSERT";
        if (method.isAnnotationPresent(Update.class) || method.getName().startsWith("update")) return "UPDATE";
        if (method.isAnnotationPresent(Delete.class) || method.getName().startsWith("delete")) return "DELETE";
        return "UNKNOWN";
    }

    // 获取原始数据
    private static Object getOriginalData(String operationType, TableInfo tableInfo, Object entity) throws ClassNotFoundException {
        if ("INSERT".equals(operationType)) return null;

        Class<?> clazz = Class.forName(tableInfo.getCurrentNamespace());
        Object idValue = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
        return ((BaseMapper<?>) SpringContextHolder.getBeanByClassSimpleName(clazz.getSimpleName()))
                .selectById(Long.valueOf(idValue.toString()));
    }

    // 异步保存日志
    @Async
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void saveAuditLogAsync(TableInfo tableInfo, Object newData,
                                  Object oldData, String operationType) {
        try {
            DataAuditLog audit = DataAuditLog.builder()
                    .tableName(tableInfo.getTableName())
                    .recordId(Long.valueOf(tableInfo.getPropertyValue(newData,
                            tableInfo.getKeyProperty()).toString()))
                    .operationType(operationType)
                    .oldData(oldData != null ? gson.toJson(oldData) : null)
                    .newData(!"DELETE".equals(operationType) ? gson.toJson(newData) : null)
                    .operator("getCurrentUser()")
                    .build();

            dataAuditLogMapper.insert(audit);
        } catch (Exception e) {
            log.error("审计日志保存失败", e);
        }
    }
}